package com.plumecache.core;

import com.plumecache.core.exception.PlumeCacheException;
import com.plumecache.core.interceptor.CacheServiceProxy;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author anson.yin
 * @date 2021/11/15 4:51 PM
 */
@Slf4j
public class CacheServiceFactory {
    private static final String TYPE_REDIS = "redis";
    private static final String TYPE_MEMCACHED = "memcached";
    private static final String TYPE_REDIS_CLUSTER = "redisCluster";

    private static volatile boolean initialized = false;

    private static CacheService defaultCacheService;
    private static final Map<String, CacheService> CACHE_SERVICE_MAP = new ConcurrentHashMap<>();

    public static Map<String, CacheService> getCacheServiceMap() {
        return CACHE_SERVICE_MAP;
    }

    private CacheServiceFactory() {
    }

    public static synchronized void initialize(List<InstanceProperties> propertiesList) {
        if (initialized) {
            log.warn("cacheServiceFactory has been loaded already!");
            return;
        }

        if (null == propertiesList || propertiesList.isEmpty()) {
            log.warn("instancePropertiesMap is empty");
            return;
        }

        propertiesList.forEach(instanceProperties -> {
                    if (StringUtils.isBlank(instanceProperties.getName())) {
                        instanceProperties.setName(instanceProperties.getType());
                    }

                    if (CACHE_SERVICE_MAP.containsKey(instanceProperties.getName())) {
                        throw new PlumeCacheException(String.format("the name [%s] has exists", instanceProperties.getName()));
                    }

                    CacheService cacheService = initialize(instanceProperties);
                    if (null == defaultCacheService) {
                        defaultCacheService = cacheService;
                    }

                    CACHE_SERVICE_MAP.put(instanceProperties.getName(), cacheService);
                }
        );

        initialized = true;
    }

    private static CacheService initialize(InstanceProperties instanceProperties) {
        CacheService cacheService;
        switch (instanceProperties.getType()) {
            case TYPE_REDIS:
                cacheService = newInstance(instanceProperties, SingleRedisCacheService.class);
                break;
            case TYPE_REDIS_CLUSTER:
                cacheService = newInstance(instanceProperties, ClusterRedisCacheService.class);
                break;
            case TYPE_MEMCACHED:
                cacheService = newInstance(instanceProperties, MemcachedCacheService.class);
                break;
            default:
                throw new PlumeCacheException("not support cache type:" + instanceProperties.getType());
        }

        return CacheServiceProxy.newProxyInstance(cacheService);
    }

    @SneakyThrows
    public static <T extends CacheService> CacheService newInstance(InstanceProperties properties, Class<T> clazz) {
        CacheService cacheService = clazz.newInstance();
        cacheService.initialize(properties);
        return cacheService;
    }

    public static CacheService getInstance() {
        return defaultCacheService;
    }

    public static CacheService getInstance(String instanceName) {
        return CACHE_SERVICE_MAP.get(instanceName);
    }

}
